/*!
\copyright  Copyright (c) 2020-2024 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\version    
\file       fast_pair_msg_stream_dev_action.c
\brief      Component handling fast pair device action.
*/

#include <bdaddr.h>

#include "fast_pair.h"
#include "fast_pair_config.h"
#include "fast_pair_msg_stream.h"
#include "fast_pair_msg_stream_dev_action.h"
#include "bt_device.h"
#include "headset_phy_state.h"
#include "multidevice.h"
#include "peer_signalling.h"
#include "fast_pair_rfcomm.h"
#include "fast_pair_ui_messages.h"
#include "phy_state.h"
#include "state_proxy.h"

#define FASTPAIR_DEVICEACTION_CODE_INDEX  0
#define FASTPAIR_DEVICEACTION_ADD_DATA_LEN_UPPER_INDEX  1
#define FASTPAIR_DEVICEACTION_ADD_DATA_LEN_LOWER_INDEX  2
#define FASTPAIR_DEVICEACTION_ADD_DATA_INDEX 3

#define FASTPAIR_DEVICEACTION_ADD_DATA_LEN 2

#define DEFAULT_RING_TIMES 5

fast_pair_msg_stream_dev_action dev_action_data;

/*! Global Instance of Ring Device Task Data */
fp_ring_device_task_data_t ring_device;

/*! Callback function to notify the registered client about ring or mute operation */
static fastPair_RingDeviceCallBack fast_pair_ring_device_callback = NULL;

/*! Definition of marshalled messages used for ring device feature. */
const marshal_member_descriptor_t fp_ring_device_req_member_descriptors[] =
{
    MAKE_MARSHAL_MEMBER(fast_pair_ring_device_req_t, uint8, ring_start_stop),
    MAKE_MARSHAL_MEMBER(fast_pair_ring_device_req_t, uint8, ring_volume),
    MAKE_MARSHAL_MEMBER(fast_pair_ring_device_req_t, uint16, ring_time),
};

const marshal_type_descriptor_t marshal_type_descriptor_fast_pair_ring_device_req_t =
    MAKE_MARSHAL_TYPE_DEFINITION(fast_pair_ring_device_req_t, fp_ring_device_req_member_descriptors);

const marshal_type_descriptor_t marshal_type_descriptor_fast_pair_ring_device_cfm_t =
    MAKE_MARSHAL_TYPE_DEFINITION_BASIC(sizeof(fast_pair_ring_device_cfm_t));

/*! X-Macro generate ring device marshal type descriptor set that can be passed to a (un)marshaller to initialise it.
 */
#define EXPAND_AS_TYPE_DEFINITION(type) (const marshal_type_descriptor_t *)&marshal_type_descriptor_##type,
const marshal_type_descriptor_t * const fp_ring_device_marshal_type_descriptors[NUMBER_OF_MARSHAL_DEVICE_ACTION_SYNC_OBJECT_TYPES] = {
    MARSHAL_COMMON_TYPES_TABLE(EXPAND_AS_TYPE_DEFINITION)
    MARSHAL_TYPES_TABLE_RING_DEVICE(EXPAND_AS_TYPE_DEFINITION)
};
#undef EXPAND_AS_TYPE_DEFINITION

static void fastPair_GetAckAdditionalData(uint8_t* ack_data, uint8_t* ack_data_len)
{
    *ack_data_len = dev_action_data.ring_timeout == 0 ? 1 : FASTPAIR_DEVICEACTION_ADD_DATA_LEN;

    if(dev_action_data.ring_timeout)
    {
        ack_data[0] = dev_action_data.ring_timeout;
        ack_data[1] = dev_action_data.ring_component;
    }
    else
    {
        ack_data[0] = dev_action_data.ring_component;
        ack_data[1] = 0;
    }
}
static bool fastPair_CanRing(void)
{
    headsetPhyState headset_state = appHeadsetPhyStateGetState();
    uint8 provider_device_type = Multidevice_GetType();

    return (provider_device_type == multidevice_type_pair && !StateProxy_IsInEar()) ||
           (provider_device_type == multidevice_type_single && headset_state != HEADSET_PHY_STATE_ON_HEAD);
}

static bool fastPair_CanPeerRing(void)
{
    uint8 provider_device_type = Multidevice_GetType();
    return (provider_device_type == multidevice_type_pair && !StateProxy_IsPeerInEar());
}


static void fastPair_RingDevice_SendMarshalledData(bool ring_start_stop, uint8 ring_volume, uint16 ring_time)
{
    fast_pair_ring_device_req_t *ring_device_data;

    if(appPeerSigIsConnected())
    {
        DEBUG_LOG("fastPair_RingDeviceTime_SendMarshalledData. Send Marshalled Data to the peer.");
        ring_device_data = PanicUnlessMalloc(sizeof(fast_pair_ring_device_req_t));

        ring_device_data->ring_start_stop = ring_start_stop;
        ring_device_data->ring_time = ring_time;
        ring_device_data->ring_volume = ring_volume;

        /*! send the ring time to counterpart on other earbud */
        appPeerSigMarshalledMsgChannelTx(fpRingDevice_GetTask(),
                                         PEER_SIG_MSG_CHANNEL_FP_RING_EVENT,
                                         ring_device_data, MARSHAL_TYPE_fast_pair_ring_device_req_t);
    }
    else
    {
        DEBUG_LOG("fastPair_RingDeviceTime_SendMarshalledData. No Peer to send to.");
    }
}

static void fastPair_RingDevice_SendConfirmation(bool synced)
{
    if (appPeerSigIsConnected())
    {
        fast_pair_ring_device_cfm_t* cfm = PanicUnlessMalloc(sizeof(fast_pair_ring_device_cfm_t));
        cfm->synced = synced;
        DEBUG_LOG("fastPair_RingDevice_SendConfirmation. Send confirmation to the peer.");
        /*! send confirmation of ring device received */
        appPeerSigMarshalledMsgChannelTx(fpRingDevice_GetTask(),
                                         PEER_SIG_MSG_CHANNEL_FP_RING_EVENT,
                                         cfm, MARSHAL_TYPE_fast_pair_ring_device_cfm_t);
    }
    else
    {
        DEBUG_LOG("fastPair_RingDevice_SendConfirmation. No Peer to send to.");
    }
}

/*! \brief Parse additional data in device action message.
 */
static void fastPair_MsgStream_RingDevAction_Set(const uint8* dev_action_ring_info, uint16 dev_action_ring_len)
{
    if (dev_action_ring_len == FASTPAIR_DEVICEACTION_ADD_DATA_LEN)
    {
        dev_action_data.ring_component = dev_action_ring_info[0];
        dev_action_data.ring_timeout = dev_action_ring_info[1];
    }
    else
    {
        dev_action_data.ring_component = dev_action_ring_info[0];
        dev_action_data.ring_timeout = 0;
    }
    DEBUG_LOG("fastPair_MsgStream_RingDevAction_Set: ring component - 0x%x", dev_action_data.ring_component);
    DEBUG_LOG("fastPair_MsgStream_RingDevAction_Set: ring duration - 0x%x", dev_action_data.ring_timeout);
}

/*! \brief When device action message to ring is received, mark device is currently ringing and
           start playing the ringtone from given ring volume.
 */
static void fastPair_RingTone_Properties_Set(uint8 ring_volume)
{
        ring_device.is_device_ring = TRUE;
        ring_device.vol_level = ring_volume;
        ring_device.ringtimes = DEFAULT_RING_TIMES;
        FastPairUiMessages_InformContextUpdate();
}

/*! \brief Keep ringing the device for duration of ring_timeout value in seconds.
 */
static void fastPair_CheckRingDevice_Timeout(void)
{
    if (dev_action_data.ring_timeout != 0)
    {           
        uint16* ring_stop = PanicUnlessMalloc(sizeof(uint16));
            
        *ring_stop = FP_STOP_RING_CURRENT;
        MessageSendLater(fpRingDevice_GetTask(), fast_pair_ring_stop_event, ring_stop, D_SEC(dev_action_data.ring_timeout));
    }
}

/*! \brief Start ringing the device and rampup the volume from low volume to max voume over time.
 */
static void fastPair_HandleRingTone(bool is_ring)
{
    if (is_ring)
    {
        DEBUG_LOG("fastPair_HandleRingTone: Ringing device.");
        FastPairUiMessages_StartRingtone(ring_device.vol_level);
        // ramp the tone volume, ringing DEFAULT_RING_TIMES at each volume level until max is reached.
        // Tone playback continues at max volume until the point it's disabled 
        if(ring_device.vol_level < ring_vol_max)
        {
            ring_device.ringtimes--;
            if(ring_device.ringtimes == 0)
            {
                ring_device.vol_level++;
                ring_device.ringtimes = DEFAULT_RING_TIMES;
            }
        }
        MessageSendLater(fpRingDevice_GetTask(), fast_pair_ring_event, NULL, D_SEC(3));
    }
    else
    {
        DEBUG_LOG("fastPair_HandleRingTone: Muting device");
        MessageCancelAll(fpRingDevice_GetTask(), fast_pair_ring_event);
        MessageCancelAll(fpRingDevice_GetTask(), fast_pair_ring_stop_event);
        FastPairUiMessages_StopRingtone();
        ring_device.is_device_ring = FALSE;
        FastPairUiMessages_InformContextUpdate();
    }
}

/*! \brief Handle incoming device action message sent by Seeker.
 */
static void fastPair_DevAction_HandleIncomingData(uint8 fp_seeker_number, const uint8* msg_data, uint16 len)
{
    uint16 additional_data_len;
    uint8 msg_code;

    if ((msg_data == NULL) || (len < FASTPAIR_DEVICEACTION_ADD_DATA_INDEX))
    {
        DEBUG_LOG_ERROR("fastPair_DevAction_HandleIncomingData: UNEXPECTED ERROR - Length is %d is less than minimum of %d or data is NULL",
                        len,FASTPAIR_DEVICEACTION_ADD_DATA_INDEX);
        return;
    }

    additional_data_len = (msg_data[FASTPAIR_DEVICEACTION_ADD_DATA_LEN_UPPER_INDEX]<<8) + 
                           msg_data[FASTPAIR_DEVICEACTION_ADD_DATA_LEN_LOWER_INDEX];


    if((FASTPAIR_DEVICEACTION_ADD_DATA_INDEX+additional_data_len) != len)
    {
        DEBUG_LOG_ERROR("fastPair_DevAction_HandleIncomingData: UNEXPECTED length ERROR Length is %d. Should be %d",
                        len,(FASTPAIR_DEVICEACTION_ADD_DATA_INDEX+additional_data_len));
        return;
    }

    msg_code = msg_data[FASTPAIR_DEVICEACTION_CODE_INDEX];
    switch(msg_code)
    {
        case FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT:
        {
          fastPair_MsgStream_RingDevAction_Set(&msg_data[FASTPAIR_DEVICEACTION_ADD_DATA_INDEX], additional_data_len);

          /* Check for number of Rfcomm Instances. If RfComm instances are two, sync the ringing status to Handsets */
          if (fastPair_RfcommGetRFCommConnectedInstances() == FASTPAIR_RFCOMM_CONNECTIONS_MAX)
          {
              /* When Ring Left/Right is initiated from AG1, send the ring device message also to AG2 and vice-versa. */
              uint8_t other_seeker = (fp_seeker_number == 1) ? 2 : 1;

              /* Send ring device message to other AG (AG1 or AG2) depending on the value of fp seeker number from which the action is performed */
              fastPair_MsgStreamSendData(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT,
                                         FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,
                                         &dev_action_data.ring_component,
                                         FASTPAIR_DEVICEACTION_RING_RSP_ADD_DATA_LEN,
                                         other_seeker);
          }

          /* Perform ring/mute device */
          FastPair_RingMuteDevice(fp_seeker_number, dev_action_data.ring_component, FASTPAIR_DEVICEACTION_RING_VOL_DEFAULT, dev_action_data.ring_timeout);
          
        }
        break;

        default:
        {
            /* Acknowledge message */
            fastPair_MsgStreamSendACK(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT,msg_code, fp_seeker_number);

            DEBUG_LOG_ERROR("fastPair_DevAction_HandleIncomingData: UNHANDLED code %d. ",msg_code);
            return;
        }
    }
}

/* Handle messages from Message stream */
static void fastPair_DevAction_MsgStreamMessageHandler(FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE msg_type,uint8 fp_seeker_number, const uint8 *msg_data, uint16 len)
{
    switch(msg_type)
    {
        case FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_CONNECT_IND:
            DEBUG_LOG("FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_CONNECT_IND");
            break;
        case FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_SERVER_CONNECT_CFM:
            DEBUG_LOG("FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_SERVER_CONNECT_CFM");
            break;
        case FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_INCOMING_DATA:
            DEBUG_LOG("FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_INCOMING_DATA");
            fastPair_DevAction_HandleIncomingData(fp_seeker_number,msg_data, len); 
            break;
        case FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_DISCONNECT_IND:
            DEBUG_LOG("FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_DISCONNECT_IND");
            break;
        case FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_DISCONNECT_CFM:
            DEBUG_LOG("FASTPAIR_MESSAGE_STREAM_MESSAGE_TYPE_DISCONNECT_IND");
            break;
        default:
            DEBUG_LOG("fastPair_DevAction_MsgStreamMessageHandler: unknown message=%x", msg_type);
            break;
    }
}

static void fastPair_RingDevice_HandleMarshalledMsgChannelTxCfm(const PEER_SIG_MARSHALLED_MSG_CHANNEL_TX_CFM_T* cfm)
{
    DEBUG_LOG("fastPair_RingDevice_HandleMarshalledMsgChannelTxCfm channel %u status %u", cfm->channel, cfm->status);
}

/*! \brief Handle incoming marshalled messages from peer ring device component.
 */
static void fastPair_RingDevice_HandleMarshalledMsgChannelRxInd(PEER_SIG_MARSHALLED_MSG_CHANNEL_RX_IND_T* ind)
{
    switch (ind->type)
    {
        case MARSHAL_TYPE_fast_pair_ring_device_req_t:
        {
            fast_pair_ring_device_req_t* req = (fast_pair_ring_device_req_t*)ind->msg;
            DEBUG_LOG("fastPair_RingDevice_HandleMarshalledMsgChannelRxInd RX Ring Device ");

            if(req->ring_start_stop == TRUE)
            {
                fastPair_RingTone_Properties_Set(req->ring_volume);
                dev_action_data.ring_timeout = req->ring_time;
                fastPair_CheckRingDevice_Timeout();
                fastPair_HandleRingTone(TRUE);
            }
            else
            {
                if (dev_action_data.ring_timeout != 0)
                {
                    MessageCancelAll(fpRingDevice_GetTask(), fast_pair_ring_stop_event);
                }
                fastPair_HandleRingTone(FALSE);
            }
            /*! Send the confirmation to the peer */
            fastPair_RingDevice_SendConfirmation(TRUE);
            free(req);
        }
        break;

        case MARSHAL_TYPE_fast_pair_ring_device_cfm_t:
        {
            fast_pair_ring_device_cfm_t *cfm = (fast_pair_ring_device_cfm_t*)ind->msg;

            /* Make sure if peer is also ringing and the ringing is syncronized between the buds. */
            if(cfm->synced)
            {
                DEBUG_LOG("fastPair_RingDevice_HandleMarshalledMsgChannelRxInd. Ring Start/Stop Successful.");
            }
            else
            {
                DEBUG_LOG("fastPair_RingDevice_HandleMarshalledMsgChannelRxInd. Failed to Ring Start/Stop.");
            }
            free(cfm);
        }
        break;

        default:
        break;
    }
}

/*! \brief This function is the message handler for fast pair ring device module.
 */
static void fastPair_RingDevice_HandleMessage(Task task, MessageId id, Message message)
{
    UNUSED(task);

    switch (id)
    {
        /* marshalled messaging */
        case PEER_SIG_MARSHALLED_MSG_CHANNEL_RX_IND:
            DEBUG_LOG("fastPair_RingDevice_HandleMessage. PEER_SIG_MARSHALLED_MSG_CHANNEL_RX_IND");
            fastPair_RingDevice_HandleMarshalledMsgChannelRxInd((PEER_SIG_MARSHALLED_MSG_CHANNEL_RX_IND_T*)message);
            break;
        case PEER_SIG_MARSHALLED_MSG_CHANNEL_TX_CFM:
            DEBUG_LOG("fastPair_RingDevice_HandleMessage. PEER_SIG_MARSHALLED_MSG_CHANNEL_TX_CFM");
            fastPair_RingDevice_HandleMarshalledMsgChannelTxCfm((PEER_SIG_MARSHALLED_MSG_CHANNEL_TX_CFM_T*)message);
            break;
        case fast_pair_ring_event:
            DEBUG_LOG("fastPair_RingDevice_HandleMessage. fast_pair_ring_event");
            fastPair_HandleRingTone(TRUE);
            break;
        case fast_pair_ring_stop_event:
        {
            fast_pair_ring_device_ringing_state ringing_state = fast_pair_ringing_state_invalid;

            DEBUG_LOG("fastPair_RingDevice_HandleMessage. fast_pair_ring_stop_event");
            if (dev_action_data.ring_timeout != 0)
            {
                MessageCancelAll(fpRingDevice_GetTask(), fast_pair_ring_stop_event);
            }

            /* When earbud goes into case or headset is powered off, stop only ringing device tone. */
            if (ring_device.is_device_ring)
            {
                fastPair_HandleRingTone(FALSE);
            }
            /* If ringing stops due to handset disconnection stop both buds */
            {
                uint16* ring_stop = (uint16*)message;
                if(*ring_stop == FP_STOP_RING_BOTH)
                {
                    uint8 provider_device_type = Multidevice_GetType();
                    if (provider_device_type == multidevice_type_pair)
                    {
                        fastPair_RingDevice_SendMarshalledData(FALSE, FASTPAIR_DEVICEACTION_RING_VOL_DEFAULT, dev_action_data.ring_timeout);
                    }
                }
            }

            /* fast_pair_ring_stop_event gets received after ring timeout, or upon physical state change - power on/off or on disconnection 
               For fast pair ring my accessory feature, seeker always sends ring timeout as ZERO, so timeout scenario is valid only when seeker 
               number is FASTPAIR_SEEKER_INVALID(which is assigned to Google Finder Handset) and for all other operations, we need to assign 
               the ringing state to 0x04 as mentioned in the Google Finder Spec.*/
            if(dev_action_data.seeker_number == FASTPAIR_SEEKER_INVALID)
            {
                ringing_state = fast_pair_ringing_stopped_timeout;
            }
            else
            {
                ringing_state = fast_pair_ringing_stopped_gatt_request;
            }

            /* Callback to let google finder know about ring/mute device operation */
            if(fast_pair_ring_device_callback != NULL)
            {
                fast_pair_ring_device_callback(dev_action_data.ring_component, ringing_state, dev_action_data.ring_timeout);
            }

            memset(&dev_action_data, 0, sizeof(dev_action_data));
        }
        break;
        default:
            DEBUG_LOG("fastPair_RingDevice_HandleMessage: unknown message=%x", id);
            break;
    }
}

static void fastPair_CancelRingDevice(void)
{
    MessageCancelAll(fpRingDevice_GetTask(), fast_pair_ring_stop_event);
    if(Multidevice_GetType() == multidevice_type_pair)
    {
        fastPair_RingDevice_SendMarshalledData(FALSE, 0, dev_action_data.ring_timeout);
    }
    fastPair_HandleRingTone(FALSE);
}

void FastPair_HandleLocalCancelRingDevice(void)
{
    DEBUG_LOG("FastPair_HandleLocalCancelRingDevice");
    fastPair_CancelRingDevice();
    if(fast_pair_ring_device_callback != NULL)
    {
        fast_pair_ring_device_callback(dev_action_data.ring_component, fast_pair_ringing_stopped_button_press, dev_action_data.ring_timeout);
    }
}


bool FastPair_RingMuteDevice(uint8 fp_seeker_number, uint8 ring_component, uint8 ring_volume, uint16 ring_timeout)
{
    DEBUG_LOG("FastPair_RingMuteDevice");
    uint8 provider_device_type = Multidevice_GetType();
    fast_pair_ring_device_ringing_state ringing_state = fast_pair_ringing_state_invalid;

    DEBUG_LOG("Provider device: 0x%x", provider_device_type);

    uint8_t nak_data_len = 1;
    uint8_t nak_data[1];
    nak_data[0] = FASTPAIR_DEVICEACTION_STOP_RING;

    if(fp_seeker_number == FASTPAIR_SEEKER_INVALID)
    {
        uint8 current_ring_component = dev_action_data.ring_component;

        if(ring_component == FASTPAIR_DEVICEACTION_RING_ALL)
        {
            dev_action_data.ring_component = FASTPAIR_DEVICEACTION_RING_RIGHT_LEFT;
        }
        /* Ring case is not supported in the current implementation */
        else if(ring_component >= FASTPAIR_DEVICEACTION_RING_CASE && ring_component < FASTPAIR_DEVICEACTION_RING_ALL)
        {
            return FALSE;
        }
        else
        {
            dev_action_data.ring_component = ring_component;
        }

        /* Send the updated ring component data to the connected FP seekers.
           eg. if left EB is already ringing and ring right is performed the data to 
           be sent to the seeker should be 0x03 */
        if(ring_component != FASTPAIR_DEVICEACTION_STOP_RING)
        {
            current_ring_component = current_ring_component | dev_action_data.ring_component;
        }
        else
        {
            current_ring_component = dev_action_data.ring_component;
        }

        fastPair_MsgStreamSendDataToAll(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT,
                                        FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,
                                        &current_ring_component,
                                        FASTPAIR_DEVICEACTION_RING_RSP_ADD_DATA_LEN);
    }

    dev_action_data.ring_timeout = ring_timeout;
    dev_action_data.seeker_number = fp_seeker_number;

    /* Do not perform any action if the ring component is invalid. For headset 0x02 is treated as invalid component */
    if ((dev_action_data.seeker_number != FASTPAIR_SEEKER_INVALID) &&((dev_action_data.ring_component > FASTPAIR_DEVICEACTION_RING_RIGHT_LEFT) || 
        ((provider_device_type == multidevice_type_single) && (dev_action_data.ring_component == FASTPAIR_DEVICEACTION_RING_LEFT_MUTE_RIGHT))))
    {
        DEBUG_LOG("FastPair_RingMuteDevice: Invalid component 0x%x", dev_action_data.ring_component);
        /* Send NACK message with reason as not supported. */
        fastPair_MsgStreamSendNAK(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT, FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,
                                  FASTPAIR_MESSAGESTREAM_NAK_REASON_NOT_SUPPORTED, fp_seeker_number);
        return FALSE;
    }

    /* Ring Headset if first byte in additional data is 0x01 or 0x03. */
    if ((provider_device_type == multidevice_type_single) &&
        ((dev_action_data.ring_component == FASTPAIR_DEVICEACTION_RING_RIGHT_MUTE_LEFT) ||
         (dev_action_data.ring_component == FASTPAIR_DEVICEACTION_RING_RIGHT_LEFT)))
    {
        /* When ring device is already initiated by AG1 do not perform ring device again from AG2 */
        if (ring_device.is_device_ring)
        {
            ringing_state = fast_pair_ringing_started;
        }
        else if(fastPair_CanRing())
        {
            fastPair_RingTone_Properties_Set(ring_volume);
            fastPair_CheckRingDevice_Timeout();
            fastPair_HandleRingTone(TRUE);
            ringing_state = fast_pair_ringing_started;
        }
        /* Ring device is not already initiated, and cannot be initiated */
        else
        {
            /* Send NACK message with reason as not allowed due to state. */
            fastPair_MsgStreamSendNAKWithAdditionalData(
                FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT, FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,
                FASTPAIR_MESSAGESTREAM_NAK_REASON_NOT_ALLOWED_DUE_TO_STATE, nak_data, nak_data_len, fp_seeker_number);
        }

        /* Callback to let google finder know about ring/mute device operation */
        if(fast_pair_ring_device_callback != NULL)
        {
            fast_pair_ring_device_callback(ring_component, ringing_state, ring_timeout);
        }

        return TRUE;
    }

    if (dev_action_data.ring_component == FASTPAIR_DEVICEACTION_RING_RIGHT_MUTE_LEFT)
    {
        /* Before performing ring right earbud, 'is_device_ring' flag is set to FALSE. When ring right earbud is initiated,
           start ringing only the right earbud. When mute left earbud is initiated, mute only the left earbud.
         */
        if (ring_device.is_device_ring == FALSE)
        {
            /* If current device is right bud, then ring the device. */
            if (!Multidevice_IsLeft())
            {
                if(fastPair_CanRing())
                {
                    fastPair_RingTone_Properties_Set(ring_volume);
                    fastPair_CheckRingDevice_Timeout();
                    fastPair_HandleRingTone(TRUE);
                }
                else
                {
                    /* Send NACK message with reason as not allowed due to state. */
                    fastPair_MsgStreamSendNAKWithAdditionalData(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT, FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,
                                            FASTPAIR_MESSAGESTREAM_NAK_REASON_NOT_ALLOWED_DUE_TO_STATE, nak_data, nak_data_len, fp_seeker_number);
                    return FALSE;
                }
            }
            /* If current device is not right bud, the peer must be the right bud. Indicate peer to ring. */
            else
            {
                if(fastPair_CanPeerRing())
                {
                    /* Second byte in additional data determines how long the device should ring. Send this info to peer. */
                    fastPair_RingDevice_SendMarshalledData(TRUE, ring_volume, dev_action_data.ring_timeout);
                }
                else
                {
                    /* Send NACK message with reason as not allowed due to state. */
                    fastPair_MsgStreamSendNAKWithAdditionalData(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT, FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,
                                            FASTPAIR_MESSAGESTREAM_NAK_REASON_NOT_ALLOWED_DUE_TO_STATE, nak_data, nak_data_len, fp_seeker_number);
                    return FALSE;
                }
            }
        }

        /* Mute left earbud if the operation is performed via message stream*/
        else
        {
            if(fp_seeker_number != FASTPAIR_SEEKER_INVALID)
            {
                if (Multidevice_IsLeft())
                {
                    fastPair_HandleRingTone(FALSE);
                }
                else
                {
                    /* Inform peer left device to mute */
                    fastPair_RingDevice_SendMarshalledData(FALSE, ring_volume, dev_action_data.ring_timeout);
                }
            }
        }

        ringing_state = fast_pair_ringing_started;

    }

    else if (dev_action_data.ring_component == FASTPAIR_DEVICEACTION_RING_LEFT_MUTE_RIGHT)
    {
        /* Before performing ring left earbud, 'is_device_ring' flag is set to FALSE. When ring left earbud is initiated,
           start ringing only the left earbud. When mute right earbud is initiated, mute only the right earbud.
         */
        if (ring_device.is_device_ring == FALSE)
        {
            /* If current device is left bud, then ring the device. */
            if (Multidevice_IsLeft())
            {
                if(fastPair_CanRing())
                {
                    fastPair_RingTone_Properties_Set(ring_volume);
                    fastPair_CheckRingDevice_Timeout();
                    fastPair_HandleRingTone(TRUE);
                }
                else
                {
                    /* Send NACK message with reason as not allowed due to state. */
                    fastPair_MsgStreamSendNAKWithAdditionalData(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT, FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,
                                            FASTPAIR_MESSAGESTREAM_NAK_REASON_NOT_ALLOWED_DUE_TO_STATE, nak_data, nak_data_len, fp_seeker_number);
                    return FALSE;
                }
            }
            /* If current device is not left bud, indicate peer to ring. */
            else
            {
                if(fastPair_CanPeerRing())
                {
                    fastPair_RingDevice_SendMarshalledData(TRUE, ring_volume, dev_action_data.ring_timeout);
                }
                else {
                    /* Send NACK message with reason as not allowed due to state. */
                    fastPair_MsgStreamSendNAKWithAdditionalData(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT, FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,
                                            FASTPAIR_MESSAGESTREAM_NAK_REASON_NOT_ALLOWED_DUE_TO_STATE, nak_data, nak_data_len, fp_seeker_number);
                    return FALSE;
                }
            }
        }

        /* Mute right earbud */
        else
        {
            if(fp_seeker_number != FASTPAIR_SEEKER_INVALID)
            {
                if (!Multidevice_IsLeft())
                {
                    fastPair_HandleRingTone(FALSE);
                }
                else
                {
                    /* Inform peer right device to mute */
                    fastPair_RingDevice_SendMarshalledData(FALSE, ring_volume, dev_action_data.ring_timeout);
                }
            }
        }

        ringing_state = fast_pair_ringing_started;

    }

    else if (dev_action_data.ring_component == FASTPAIR_DEVICEACTION_RING_RIGHT_LEFT)
    {
        /* Mute the device first and start ringing the buds in sync. */
        fastPair_RingDevice_SendMarshalledData(FALSE, ring_volume, dev_action_data.ring_timeout);
        fastPair_HandleRingTone(FALSE);

        /* Set ringtone properties */
        fastPair_RingTone_Properties_Set(ring_volume);
        fastPair_CheckRingDevice_Timeout();

        if(!fastPair_CanRing() && !fastPair_CanPeerRing())
        {
            /* Send NACK message with reason as not allowed due to state. */
            fastPair_MsgStreamSendNAKWithAdditionalData(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT, FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,
                                    FASTPAIR_MESSAGESTREAM_NAK_REASON_NOT_ALLOWED_DUE_TO_STATE, nak_data, nak_data_len, fp_seeker_number);
            return FALSE;
        }

        /* Ring both the device. */
        if(fastPair_CanRing())
        {
            fastPair_HandleRingTone(TRUE);
            ringing_state = fast_pair_ringing_started;
        }
        if(fastPair_CanPeerRing())
        {
            fastPair_RingDevice_SendMarshalledData(TRUE, ring_volume, dev_action_data.ring_timeout);
            ringing_state = fast_pair_ringing_started;
        }
    }
    /* Mute Operation */
    else
    {
        fastPair_CancelRingDevice();
        ringing_state = fast_pair_ringing_stopped_gatt_request;
    }

    /* Callback to let google finder know about ring/mute device operation */
    if(fast_pair_ring_device_callback != NULL)
    {
        fast_pair_ring_device_callback(dev_action_data.ring_component, ringing_state, dev_action_data.ring_timeout);
    }

    uint8_t ack_data_len;
    uint8_t ack_data[FASTPAIR_DEVICEACTION_ADD_DATA_LEN];
    fastPair_GetAckAdditionalData(ack_data, &ack_data_len);
    fastPair_MsgStreamSendACKWithAdditionalData(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT,
                                                FASTPAIR_MESSAGESTREAM_DEVACTION_RING_EVENT,ack_data,
                                                ack_data_len, fp_seeker_number);
    return TRUE;
}

void FastPair_RegisterRingDeviceCallback(fastPair_RingDeviceCallBack ring_callback)
{
    fast_pair_ring_device_callback = ring_callback;
}

/*! \brief Fast Pair Device Action Initialization.
 */
void fastPair_MsgStreamDevAction_Init(void)
{
    DEBUG_LOG("fastPair_MsgStreamDevAction_Init");
    memset(&dev_action_data, 0, sizeof(dev_action_data));
    fp_ring_device_task_data_t *ring_device_task_data = fpRingDevice_GetTaskData();

    /* Initialize component task data */
    memset(ring_device_task_data, 0, sizeof(*ring_device_task_data));
    ring_device_task_data->task.handler = fastPair_RingDevice_HandleMessage;

    /* Register with peer signalling to use ring device msg channel */
    appPeerSigMarshalledMsgChannelTaskRegister(fpRingDevice_GetTask(),
                                               PEER_SIG_MSG_CHANNEL_FP_RING_EVENT,
                                               fp_ring_device_marshal_type_descriptors,
                                               NUMBER_OF_MARSHAL_DEVICE_ACTION_SYNC_OBJECT_TYPES);
    /* Handle Device Action messages from Message stream */
    fastPair_MsgStreamRegisterGroupMessages(FASTPAIR_MESSAGESTREAM_MESSAGE_GROUP_DEVICE_ACTION_EVENT,
                                            fastPair_DevAction_MsgStreamMessageHandler);
}
